﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Tcp;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Ftp
{
    /// <summary>
    /// ftp客户端主动连接ftp服务器
    /// </summary>
    internal class FtpPassiveDataTranfer : FtpDataTranferAbs
    {
        private readonly NetTcpListener _tcpListener;
        private Socket _client = null;
        private readonly AutoResetEvent _eventHandler = new AutoResetEvent(false);
        private readonly int _port;

        /// <summary>
        /// 构造函数
        /// </summary>
        public FtpPassiveDataTranfer(FtpSession session)
            : base(session)
        {
            List<int> usedPorts = NetHelper.GetUsedPorts();
            var config = session.FtpServer.Config;
            ushort port = config.PassiveMinPort;
            ushort maxPort = config.PassiveMaxPort;
            var ignorePortList = config.PassiveIgnorePortList;

            while (true)
            {
                while (usedPorts.Contains(port) ||
                    (ignorePortList != null && ignorePortList.Contains(port)))
                {
                    port++;

                    if (port > maxPort)
                    {
                        throw new ApplicationException("Passive模式下,没有可用的端口号");
                    }
                }

                try
                {
                    var initPara = new NetTcpListenerInitPara(port, config.Backlog);
                    //initPara.AcceptMode = ThreadMode.Sync;
                    //initPara.SyncThreadName = $"ftp-Passive-{session.FtpUser.UserName}tcp监听线程";
                    this._tcpListener = new NetTcpListener(this.AcceptSocketCallback, initPara);
                    this._tcpListener.Start();
                    ZLoger.Debug($"FtpPassive模式监听端口号\"{port}\"开启监听");
                    break;
                }
                catch (Exception ex)
                {
                    ZLoger.Warn(ex, $"FtpPassive模式监听端口号\"{port}\"被抢占,重新分配有效的端口号");
                }
            }

            this._port = port;
        }

        private void AcceptSocketCallback(AcceptSocketArgs args)
        {
            try
            {
                this._client = args.Client;
                this._tcpListener.Stop();
                this._tcpListener.Dispose();
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);

                try
                {
                    args.Client.Dispose();
                }
                catch (Exception)
                {

                }
            }

            try
            {
                this._eventHandler.Set();
            }
            catch (ObjectDisposedException)
            {

            }
        }

        /// <summary>
        /// 创建tcp连接
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="append">true:追加数据到已存在的文件,文件不存在则创建;false:总是创建新文件,如果旧文件存在则替换</param>
        /// <returns>创建的tcp连接</returns>
        protected override FtpTransferClient PrimitiveCreateTcpClient(string filePath, bool append)
        {
            if (this._eventHandler.WaitOne(base._session.FtpServer.Config.PassiveMillisecondsTimeout))
            {
                return new FtpTransferClient(this._client, this._session, append, filePath);
            }
            else
            {
                this._tcpListener.Stop();
                this._tcpListener.Dispose();
                throw new TimeoutException("ftp在Passive模式下,创建tcp连接超时");
            }
        }


        /// <summary>
        /// 获取ftp响应
        /// </summary>
        /// <returns>ftp响应</returns>
        protected override IFtpResponse PrimitiveGetFtpResponse()
        {
            //Response: 227 Entering Passive Mode (192,168,1,20,192,20).
            string ip = NetHelper.GetIPString(base._session.LinkInfo.LocalEndPoint);
            ip = ip.Replace('.', ',');
            return new FtpCodeResponse(FtpStatusCodes.Code227, $"Entering Passive Mode ({ip},{this._port / 256},{this._port % 256}).");
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        /// <param name="disposing">disposing</param>
        protected override void Dispose(bool disposing)
        {
            this._tcpListener.Dispose();
            this._eventHandler.Dispose();
        }
    }
}
